#include "EternityIISolver.h"
#include <stdio.h>



EternityIISolver::EternityIISolver()
{
	_sizeX = 0;
	_sizeY = 0;
	_nbSolutions = 0;
	_Solutions=fopen("EternityII.txt","wt");
	_nbPiecesPoser = 0;
	filename = "e2pieces.txt"; 
}


EternityIISolver::~EternityIISolver()
{
	if (_Solutions) fclose(_Solutions);
}


void EternityIISolver::readPieces()
{
	FILE* f=fopen(filename,"rt");
	int a=0;
	int b=0;
	int c=0;
	int d=0;
	if (NULL!=f)
	{
		int nbedges = 0;
		fscanf(f,"%i", &_sizeX);
		fscanf(f,"%i", &_sizeY);		
		fscanf(f,"%i", &nbedges);
		
		int i=0;
	    int j=0;
		_PiecesByValue = new vector<RotPieces*>**[nbedges+1];
		_BorderPiecesByValue = new vector<RotPieces*>**[nbedges+1];
		_RightBorderPiecesByValue = new vector<RotPieces*>**[nbedges+1];
        _BottomBorderPiecesByValue = new vector<RotPieces*>**[nbedges+1];
		_CornerPiecesByValue = new vector<RotPieces*>**[nbedges+1];
		for (i=0;i<nbedges+1;i++)
		{
			_PiecesByValue[i] = new vector<RotPieces*>*[nbedges+1];
			_BorderPiecesByValue[i] = new vector<RotPieces*>*[nbedges+1];
			_CornerPiecesByValue[i] = new vector<RotPieces*>*[nbedges+1];
			_RightBorderPiecesByValue[i] = new vector<RotPieces*>*[nbedges+1];
            _BottomBorderPiecesByValue[i] = new vector<RotPieces*>*[nbedges+1];
			for (j=0;j<nbedges+1;j++)
			{
				_PiecesByValue[i][j] = new vector<RotPieces*>();
				_BorderPiecesByValue[i][j] = new vector<RotPieces*>();
				_CornerPiecesByValue[i][j] = new vector<RotPieces*>();
				_RightBorderPiecesByValue[i][j] = new vector<RotPieces*>();
				_BottomBorderPiecesByValue[i][j] = new vector<RotPieces*>();
			}
		}	
	
		int nbPoints = (_sizeX+1)*(_sizeY) + (_sizeX)*(_sizeY+1) ;
		_valueSolution = new int[nbPoints];

		int nbPoints2Lignes = 1+2*_sizeY;

		for(i=0;i<nbPoints;i++)
		{
			// les bords !!!
			if  ( 
				   (i<_sizeY) 
				|| ( (i%nbPoints2Lignes)==(_sizeY)  ) 
				|| ( (i%nbPoints2Lignes)==(nbPoints2Lignes-1) ) 
				|| ( (i>(nbPoints-_sizeY-1) ))
				)
				
				_valueSolution[i] = 0;
			else 
				_valueSolution[i] = -1;
		}	

		int nbPieces = _sizeX*_sizeY;

		_pointeurSolutions = new Piece*[nbPieces];

		for(int k=0;k<nbPieces;k++)
		{			
		    i = k / _sizeY;
			j = k % _sizeY;
			_pointeurSolutions[k] = new Piece();
			_pointeurSolutions[k]->top = &_valueSolution[i*nbPoints2Lignes+j];
			_pointeurSolutions[k]->right = &_valueSolution[i*nbPoints2Lignes+j+_sizeY+1];
			_pointeurSolutions[k]->bottom = &_valueSolution[i*nbPoints2Lignes+nbPoints2Lignes+j];
			_pointeurSolutions[k]->left = &_valueSolution[i*nbPoints2Lignes+j+_sizeY];
			
			if  ( ((i==0) && (j==0)) || (i==(_sizeX-1)) && (j==(_sizeY-1)) || (i==(_sizeX-1)) && (j==0)
				|| (i==0) && (j==(_sizeY-1)) ) _pointeurSolutions[k]->type = CORNER;
			else if  ( (i==0) || (j==0) ) _pointeurSolutions[k]->type = UP_OR_LEFT_BORDER;
			else if  (i==(_sizeX-1))_pointeurSolutions[k]->type =  BOTTOM_BORDER;
			else if  (j==(_sizeY-1)) _pointeurSolutions[k]->type = RIGHT_BORDER;
			else _pointeurSolutions[k]->type = NORMAL;
		}

		int nbzeros = 0;

		while(!feof(f))
		{

		    nbzeros = 0;
			fscanf(f,"%i", &a);
			if (a==0) nbzeros++;
			fscanf(f,"%i", &b);
			if (b==0) nbzeros++;
			fscanf(f,"%i", &c);
			if (c==0) nbzeros++;
			fscanf(f,"%i", &d);			
			if (d==0) nbzeros++;	

			RotPieces* rotPieces1 = new RotPieces();	
			rotPieces1->left = a;
			rotPieces1->top = b;
			rotPieces1->right = c;
			rotPieces1->bottom = d;			
			rotPieces1->isUsed = new bool(false);			

			RotPieces* rotPieces2 = new RotPieces();
			rotPieces2->left = b;
			rotPieces2->top = c;
			rotPieces2->right = d;
			rotPieces2->bottom = a;			
			rotPieces2->isUsed =  rotPieces1->isUsed;		

			RotPieces* rotPieces3 = new RotPieces();
			rotPieces3->left = c;
			rotPieces3->top = d;
			rotPieces3->right = a;
			rotPieces3->bottom = b;			
			rotPieces3->isUsed =  rotPieces1->isUsed;	

			RotPieces* rotPieces4 = new RotPieces();
			rotPieces4->left = d;
			rotPieces4->top = a;
			rotPieces4->right = b;
			rotPieces4->bottom = c;			
			rotPieces4->isUsed =  rotPieces1->isUsed;		

			switch (nbzeros)
			{
				case 0:
					{
						_PiecesByValue[a][b]->push_back(rotPieces1);
						_PiecesByValue[b][c]->push_back(rotPieces2);
						_PiecesByValue[c][d]->push_back(rotPieces3);
						_PiecesByValue[d][a]->push_back(rotPieces4);
						break;
					}
				case 1:
					{						
						if (0==c){_RightBorderPiecesByValue[a][b]->push_back(rotPieces1);}
						else if (0==d) {_BottomBorderPiecesByValue[a][b]->push_back(rotPieces1);}
						else {_BorderPiecesByValue[a][b]->push_back(rotPieces1);}

						if (0==d) {_RightBorderPiecesByValue[b][c]->push_back(rotPieces2);}
						else if (0==a) {_BottomBorderPiecesByValue[b][c]->push_back(rotPieces2);}
						else { _BorderPiecesByValue[b][c]->push_back(rotPieces2);}

						if (0==a){ _RightBorderPiecesByValue[c][d]->push_back(rotPieces3);}
						else if (0==b) {_BottomBorderPiecesByValue[c][d]->push_back(rotPieces3);}
						else { _BorderPiecesByValue[c][d]->push_back(rotPieces3);}

						if (0==b){ _RightBorderPiecesByValue[d][a]->push_back(rotPieces4);}
						else if (0==c){ _BottomBorderPiecesByValue[d][a]->push_back(rotPieces4);}
						else  {_BorderPiecesByValue[d][a]->push_back(rotPieces4);}				
						break;
					}
				case 2:
					{						
						_CornerPiecesByValue[a][b]->push_back(rotPieces1);
						_CornerPiecesByValue[b][c]->push_back(rotPieces2);
						_CornerPiecesByValue[c][d]->push_back(rotPieces3);
						_CornerPiecesByValue[d][a]->push_back(rotPieces4);
						break;
					}
				default: break;
			}		
		}
		fclose(f);
	}
}

void EternityIISolver::solve()
{   
   _iterPointeurSolutions =  _pointeurSolutions;   
   subSolve(_sizeX*_sizeY);
}

void EternityIISolver::subSolve(int piecesRestantes)
{
   _nbPiecesPoser++;

   if (0==piecesRestantes)
   {
	   printSolution();		
	   printStats();
		_nbSolutions++;	
		return;
   } 
 
   vector<RotPieces*>* l_rotPieces =0;
  
   	switch ((*_iterPointeurSolutions)->type)
	{
		case NORMAL:
			{
				l_rotPieces = _PiecesByValue[*((*_iterPointeurSolutions)->left)][*((*_iterPointeurSolutions)->top)];		
				break;
			}
		case UP_OR_LEFT_BORDER:
			{						
				l_rotPieces = _BorderPiecesByValue[*((*_iterPointeurSolutions)->left)][*((*_iterPointeurSolutions)->top)];
				break;
			}
		case RIGHT_BORDER:
			{						
				l_rotPieces = _RightBorderPiecesByValue[*((*_iterPointeurSolutions)->left)][*((*_iterPointeurSolutions)->top)];
				break;
			}
		case BOTTOM_BORDER:
			{						
				l_rotPieces = _BottomBorderPiecesByValue[*((*_iterPointeurSolutions)->left)][*((*_iterPointeurSolutions)->top)];
				break;
			}
		case CORNER:
			{						
				l_rotPieces = _CornerPiecesByValue[*((*_iterPointeurSolutions)->left)][*((*_iterPointeurSolutions)->top)];		
				break;
			}
		default: break;
	}

   RotPieces* p = 0;
   vector<RotPieces*>::iterator theIterator; 

   for( theIterator = l_rotPieces->begin(); theIterator != l_rotPieces->end(); theIterator++ ) 
   {
	    p = (*theIterator);
		
		if (!(*p->isUsed)) 
		{							
			(*p->isUsed) = true;				
			*((*_iterPointeurSolutions)->bottom) = p->bottom;
			*((*_iterPointeurSolutions)->right) = p->right;
			_iterPointeurSolutions++;
			subSolve(piecesRestantes-1);
			_iterPointeurSolutions--;
			(*p->isUsed) = false;					
		}
			
   }     	
}

